﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using LINQPad.OpenAccess.Helpers;
using Telerik.OpenAccess.Metadata.Fluent;

namespace LINQPad.OpenAccess.Metadata.Reflection.Locators
{
    /// <summary>
    ///   <see cref="IMetadataSourceLocator" /> for fluent metadata sources.
    /// </summary>
    public class FluentMetadataSourceLocator : IMetadataSourceLocator
    {
        #region Private

        #region - Vars

        [DebuggerBrowsable( DebuggerBrowsableState.Never )]
        private readonly IReflectionService _reflectionService;

        #endregion

        #region - Functions

        /// <summary>
        /// Creates the metadata source info with source
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="metadataSource">The metadata source.</param>
        /// <returns>the metadata source info.</returns>
        private IMetadataSourceInfoWithSource CreatesMetadataSourceInfoWithSource( Type type, FluentMetadataSource metadataSource )
        {
            return new MetadataSourceInfoWithSource( type.Name, type.Namespace, metadataSource );
        }
        /// <summary>
        /// Determines whether the specified type is a custom fluent metadata source type.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns>
        ///   <c>true</c> if the type is a custom fluent metadata source type; otherwise, <c>false</c>.
        /// </returns>
        private bool IsCustomFluentMetadataSourceType( Type type )
        {
            if ( type == null || type.IsValueType )
                return false;
            return type.IsSubclassOf( typeof( FluentMetadataSource ) );
        }

        #endregion

        #endregion

        #region Public

        #region - Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="FluentMetadataSourceLocator" /> class.
        /// </summary>
        /// <param name="reflectionService">The reflection service.</param>
        public FluentMetadataSourceLocator( IReflectionService reflectionService )
        {
            Guard.ArgumentNotNull( reflectionService, "reflectionService" );

            this._reflectionService = reflectionService;
        }

        #endregion

        #endregion

        #region IMetadataSourceLocator Members

        /// <summary>
        /// Gets the metadata source infos from the specified assembly.
        /// </summary>
        /// <param name="assembly">The assembly.</param>
        /// <returns>
        /// The metadata source infos.
        /// </returns>
        public IEnumerable<IMetadataSourceInfoWithSource> GetFromAssembly( Assembly assembly )
        {
            Guard.ArgumentNotNull( assembly, "assembly" );

            var metadataSources = this._reflectionService
                                        .GetMetadataSourceTypesFromAssembly( assembly )
                                        .Where( IsCustomFluentMetadataSourceType )
                                        .Select( t => new { Type = t, Ctor = t.GetConstructor( Type.EmptyTypes ) } )
                                        .Where( t => t.Ctor != null ) // new() - parameterless ctor
                                        .Select
                                            (
                                                p => CreatesMetadataSourceInfoWithSource
                                                            (
                                                                p.Type
                                                                , ( FluentMetadataSource ) p.Ctor.Invoke( Type.EmptyTypes )
                                                            )
                                            );

            return metadataSources;
        }

        #endregion
    }
}